package com.goktech.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.goktech.entityDto.UserDto;
import com.goktech.pojo.Register;
import com.goktech.pojo.UpdatePass;
import com.goktech.pojo.User;
import com.goktech.mapper.UserMapper;
import com.goktech.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.goktech.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;
import xin.altitude.cms.common.entity.AjaxResult;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.goktech.utils.Constants.*;

/**
 * <p>
 * 用户实体类 服务实现类
 * </p>
 *
 * @author 初茶
 * @since 2023-05-14
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result<String> register(Register register) {
        if (ObjectUtils.isEmpty(register.getUUsername())) {
            return Result.fail(USER_EMPTY, "1");
        }
        if (ObjectUtils.isEmpty(register.getUPassword())) {
            return Result.fail(PASSWORD_EMPTY, "2");
        }
        if (ObjectUtils.isEmpty(register.getReuPassword())) {
            return Result.fail(RE_PASSWORD_EMPTY, "3");
        }
        if (ObjectUtils.isEmpty(register.getCode())) {
            return Result.fail(CODE_EMPTY, "4");
        }
        // 查询注册的用户是否已经存在
        User user = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUUsername, register.getUUsername()));
        // 用户已经存在
        if (!ObjectUtils.isEmpty(user)) {
            return Result.fail(USER_EXIST, "1");
        }
        // 验证密码复杂度
        if (RegexUtils.isPasswordInvalid(register.getUPassword())) {
            // 不符合密码复杂度
            return Result.fail(PASSWORD_VERIFY_ERROR, "2");
        }
        // 验证新旧密码一致
        if (!register.getUPassword().equals(register.getReuPassword())) {
            return Result.fail(PASSWORD_DISCORD, "3");
        }
        // 验证码效验
        String code = register.getCode();
        String redisCode = stringRedisTemplate.opsForValue().get(REGISTER_CODE);
        if (redisCode == null) {
            return Result.fail(CODE_EXPIRE, "4");
        }
        if (!code.equals(redisCode)) {
            return Result.fail(CODE_ERROR, "4");
        }
        // 用户不存在, 创建用户
        User newUser = new User();
        String userId = RandomUtil.randomNumbers(11);
        newUser.setUId(Long.parseLong(userId));
        newUser.setUUsername(register.getUUsername());
        // 加密密码
        String encode = PasswordEncoder.encode(register.getUPassword());
        newUser.setUPassword(encode);
        int row = baseMapper.insert(newUser);
        if (row > 0) {
            return Result.ok(REGISTER_SUCCESS);
        }
        return Result.fail(REGISTER_ERROR);
    }

    @Override
    public Result<String> login(User user) {
        if (ObjectUtils.isEmpty(user.getUUsername())) {
            return Result.fail(USER_EMPTY, "1");
        }
        if (ObjectUtils.isEmpty(user.getUPassword())) {
            return Result.fail(PASSWORD_EMPTY, "2");
        }
        LambdaQueryWrapper<User> gtWrapper = Wrappers.lambdaQuery(User.class).eq(User::getUUsername, user.getUUsername());
        User one = baseMapper.selectOne(gtWrapper);
        // 用户不存在
        if (ObjectUtils.isEmpty(one)) {
            return Result.fail(USER_NULL, "1");
        }
        // 用户存在, 验证密码
        Boolean matches = PasswordEncoder.matches(one.getUPassword(), user.getUPassword());
        // 密码不正确
        if (!matches) {
            return Result.fail(PASSWORD_ERROR, "2");
        }
        // 验证成功, 生成 token
        String token = CreateJwt.getToken(one);
        // 从 user 拷贝部分属性到 UserDTO
        UserDto userDto = BeanUtil.copyProperties(one, UserDto.class);
        // 将 UserDTO 转 map
        Map<String, Object> map = BeanUtil.beanToMap(userDto, new HashMap<>(), CopyOptions.create()
                .ignoreNullValue()
                .setFieldValueEditor((fieldName, fieldValue) -> {
                    if (fieldValue == null) {
                        fieldValue = "无";
                    } else {
                        fieldValue = fieldValue.toString();
                    }
                    return fieldValue;
                }));
        // 保存用户信息到 ThreadLocal 中
        UserHolder.saveUser(userDto);
        // 保存用户信息到 redis 中
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token, map);
        // 设置 token 有效期, 30分钟
        stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.SECONDS);
        // 返回 token 到前端存储
        return Result.ok(token);
    }

    @Override
    public Result<String> updatePass(UpdatePass updatePass) {
        if (ObjectUtils.isEmpty(updatePass.getUPassword())) {
            return Result.fail(PASSWORD_EMPTY, "1");
        }
        if (ObjectUtils.isEmpty(updatePass.getNewPassword())) {
            return Result.fail(NEW_PASSWORD_EMPTY, "2");
        }
        if (ObjectUtils.isEmpty(updatePass.getReuPassword())) {
            return Result.fail(RE_PASSWORD_EMPTY, "3");
        }
        if (ObjectUtils.isEmpty(updatePass.getCode())) {
            return Result.fail(CODE_EMPTY, "4");
        }
        User one = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUId, updatePass.getUserId()));
        // 验证密码
        Boolean matches = PasswordEncoder.matches(one.getUPassword(), updatePass.getUPassword());
        // 密码不正确
        if (!matches) {
            return Result.fail(PASSWORD_ERROR, "1");
        }
        // 密码正确, 验证密码复杂度
        if (RegexUtils.isPasswordInvalid(updatePass.getNewPassword())) {
            // 不符合密码复杂度
            return Result.fail(PASSWORD_VERIFY_ERROR, "2");
        }
        // 符合密码复杂度, 验证新旧密码一致
        if (!updatePass.getNewPassword().equals(updatePass.getReuPassword())) {
            return Result.fail(PASSWORD_DISCORD, "3");
        }
        // 验证码效验
        String code = updatePass.getCode();
        String redisCode = stringRedisTemplate.opsForValue().get(UPDATE_PASS_CODE + UserHolder.getUser().getUId());
        if (redisCode == null) {
            return Result.fail(CODE_EXPIRE, "4");
        }
        if (!code.equals(redisCode)) {
            return Result.fail(CODE_ERROR, "4");
        }
        // 所有信息验证成功, 更新用户密码
        User user = new User();
        user.setUId(UserHolder.getUser().getUId());
        String encode = PasswordEncoder.encode(updatePass.getNewPassword());
        user.setUPassword(encode);
        int row = baseMapper.updateById(user);
        if (row > 0) {
            return Result.ok(UPDATE_SUCCESS);
        }
        return Result.fail(UPDATE_ERROR);
    }

    @Override
    public Result<String> updateUserInfo(User userInfo, HttpServletRequest request) {
        if (!UserHolder.getUser().getUUsername().equals(userInfo.getUUsername())) {
            User one = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUUsername, userInfo.getUUsername()));
            if (one != null) {
                return Result.fail(USER_EXIST);
            }
        }
        userInfo.setUId(UserHolder.getUser().getUId());
        int row = baseMapper.updateById(userInfo);
        if (row > 0) {
            User user = baseMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUId, UserHolder.getUser().getUId()));
            UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
            // 将 UserDTO 转 map
            Map<String, Object> map = BeanUtil.beanToMap(userDto, new HashMap<>(), CopyOptions.create()
                    .ignoreNullValue()
                    .setFieldValueEditor((fieldName, fieldValue) -> {
                        if (fieldValue == null) {
                            fieldValue = "无";
                        } else {
                            fieldValue = fieldValue.toString();
                        }
                        return fieldValue;
                    }));
            // 获取请求头中的 token
            String authorization = request.getHeader("Authorization");
            // 获取真实的 token
            String token = authorization.substring(7);
            // 保存用户信息到 redis 中
            stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token, map);
            // 设置 token 有效期, 30分钟
            stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.SECONDS);
            return Result.ok(UPDATE_SUCCESS);
        }
        return Result.fail(UPDATE_ERROR);
    }

    @Override
    public Result<String> uploadHeader(MultipartFile headerFile) {
        if (headerFile.isEmpty()) Result.fail(UPLOAD_FILE_EMPTY);
        // 处理上传的文件名 = 当前时间毫秒数 + 上传文件名
        String originalFilename = RandomUtil.randomNumbers(11) + headerFile.getOriginalFilename();
        // 上传文件的路径
        String filePath =
                System.getProperty("user.dir") +
                        System.getProperty("file.separator") + "img" +
                        System.getProperty("file.separator") + "consumerPic";
        System.out.println("filePath:" + filePath);
        File file = new File(filePath);
        // 如果文件路径不存在, 则新增该路径
        if (!file.exists()) {
            boolean result = file.mkdir();
            if (!result) log.error("创建文件路径失败");
        }
        // 上传文件的真实路径
        String fileRealPath = filePath + System.getProperty("file.separator") + originalFilename;
        System.out.println("fileRealPath:" + fileRealPath);
        File file1 = new File(fileRealPath);
        // 上传至数据库头像的绝对路径
        String storeHeaderPath = "http://localhost:9999/img/consumerPic/" + originalFilename;
        try {
            headerFile.transferTo(file1);
            User user = new User();
            Long uId = UserHolder.getUser().getUId();
            user.setUId(uId);
            user.setUImg(storeHeaderPath);
            System.out.println("user:" + user);
            String conditionalPicPath = this.getById(uId).getUImg();
            if (!conditionalPicPath.equals("http://localhost:9999/img/consumerPic/header.png")) {
                String realPath = System.getProperty("user.dir") + conditionalPicPath.replace("http://localhost:9999", "");
                File realFile = new File(realPath);
                boolean delete = realFile.delete();
            }
            int row = baseMapper.updateById(user);
            if (row > 0) {
                return Result.ok(UPLOAD_SUCCESS);
            }
            return Result.fail(UPLOAD_ERROR);
        } catch (IOException e) {
            System.out.println(e.getMessage());
            return Result.fail(UPLOAD_ERROR + e.getMessage());
        }
    }

    @Override
    public void imageGenerate(HttpServletResponse response) {
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(150, 45, 4, 4);
        try {
            captcha.write(response.getOutputStream());
            //获取验证码内容
            String code = captcha.getCode();
            System.out.println(code);
            // 存入 redis
            stringRedisTemplate.opsForValue().set(REGISTER_CODE, code, CODE_TTL, TimeUnit.MINUTES);
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void updPassCode(HttpServletResponse response, Long uId) {
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(150, 45, 4, 4);
        try {
            captcha.write(response.getOutputStream());
            // 获取验证码内容
            String code = captcha.getCode();
            System.out.println("updPassCode:" + code);
            // 存入 redis
            stringRedisTemplate.opsForValue().set(UPDATE_PASS_CODE + uId, code, CODE_TTL, TimeUnit.MINUTES);
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
